package org.syntaxlisp.core.match.tree;

import org.syntaxlisp.core.element.SimplifyRepeatElement;
import org.syntaxlisp.core.utils.Copyable;
import org.syntaxlisp.core.utils.ListTreeVal;
import lombok.*;

/**
 * @author HuangYijun
 * @data 2023-08-03 18:30
 **/
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RepeatFrame implements Copyable<RepeatFrame>, ListTreeVal {

    @Getter
    private MatchState repeatState;

    @Setter
    @Getter
    private NewMatchSeqNode latestPreMatchBodySeqNode;

    private int matchTime;

    @Setter
    @Getter
    private int enterOffset;

    private String key;

    private Integer keyCode;

    private Integer listTreeKeyCode;

    public RepeatFrame copyForReduce () {
        return RepeatFrame.builder()
                          .repeatState(repeatState)
                          .latestPreMatchBodySeqNode(latestPreMatchBodySeqNode)
                          .matchTime(matchTime)
                          .enterOffset(enterOffset)
                          .build();
    }

    /**
     * 尝试 increase 1 time
     * @return 是否达到匹配最大值
     */
    public boolean increase () {
        SimplifyRepeatElement repeatElement = (SimplifyRepeatElement) repeatState.getSrc();
        matchTime += 1;
        return repeatElement.reachMax(matchTime);
    }

    public boolean reachMax () {
        SimplifyRepeatElement repeatElement = (SimplifyRepeatElement) repeatState.getSrc();
        return repeatElement.reachMax(matchTime);
    }

    public RepeatFrame copy () {
        return RepeatFrame.builder()
                          .repeatState(repeatState)
                          .matchTime(matchTime)
                          .enterOffset(enterOffset)
                          .latestPreMatchBodySeqNode(latestPreMatchBodySeqNode)
                          .build();
    }

    public boolean equals (Object o) {
        if (!(o instanceof RepeatFrame)) {
            return false;
        }
        RepeatFrame orf = (RepeatFrame) o;
        return isSameRepeatState(orf.repeatState) && orf.matchTime == matchTime && orf.enterOffset == enterOffset;
    }

    public String getTrace () {
        return repeatState.getSrc().getId() + "-" + matchTime + "-" + enterOffset;
    }

    public boolean isSameRepeatState(MatchState matchState) {
        return repeatState.getSrc().equals(matchState.getSrc());
    }

    public boolean fit () {
        return ((SimplifyRepeatElement) repeatState.getSrc()).fit(matchTime);
    }

    @Override
    public String getUniqueKey() {
        if (key == null) {
            key = repeatState.getSrc().getId()
                  + ","
                  + enterOffset
                  + ","
                  + matchTime
                  + ","
                  + ((latestPreMatchBodySeqNode != null)
                     ? latestPreMatchBodySeqNode.getKey()
                     : "");
        }
        return key;
    }

    @Override
    public Integer getHashKey() {
        if (keyCode == null) {
            keyCode = getUniqueKey().hashCode();
        }
        return keyCode;
    }

    @Override
    public RepeatFrame getSrcVal() {
        return this;
    }

    public boolean isLongestMatchRepeat () {
        return repeatState.isGreedyMatch();
    }

    public void accessRepeatCurStart(int enterOffset, NewMatchSeqNode latestPreMatchBodySeqNode) {
        setEnterOffset(enterOffset);
        setLatestPreMatchBodySeqNode(latestPreMatchBodySeqNode);
        key = repeatState.getSrc().getId() + "," + enterOffset + "," + matchTime;
    }
}
